home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / programming / c / hce.lha / HCE / HCESource / TOP / Source / reg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-02  |  17.8 KB  |  852 lines

  1. /* Copyright (c) 1989,1991 by Sozobon, Limited.  Author: Tony Andrews
  2.  *
  3.  * Permission is granted to anyone to use this software for any purpose
  4.  * on any computer system, and to redistribute it freely, with the
  5.  * following restrictions:
  6.  * 1) No charge may be made other than reasonable charges for reproduction.
  7.  * 2) Modified versions must be clearly marked as such.
  8.  * 3) The authors are not responsible for any harmful consequences
  9.  *    of using this software, even if they result from defects in it.
  10.  */
  11.  
  12. /*
  13.  * The code in this file deals with "registerizing" local variables and
  14.  * parameters. The general idea is to look for highly referenced local
  15.  * variables and parameters and effectively turn them into register
  16.  * variables automatically. Only the D registers are used, currently, so
  17.  * for pointer variables, a manual "register" declaration in the source
  18.  * code is actually better.
  19.  *
  20.  * We need to be certain of several things about a variable before placing
  21.  * it in a register. It's address must not be taken, and it must not be
  22.  * referred to through "aliases" (e.g. when casting to a shorter object).
  23.  * It must be able to fit in a register. And to keep things like printf from
  24.  * breaking, parameters can only be registerized if none of the parameters
  25.  * have their address taken.
  26.  *
  27.  * The compiler makes this all possible by placing "hints" within the
  28.  * generated assembly code. These hints appear as comments, but are parsed
  29.  * by the optimizer, and the information is stashed away by calling addvar().
  30.  * The hints give us the size and offset of each parameter and local variable.
  31.  * Their names are also given, although that information isn't needed here.
  32.  *
  33.  * There are tradeoffs to be wary of when registerizing. If no register
  34.  * variables exist yet, then "movem" instructions have to be added, requiring
  35.  * more references to make this worthwhile. In the case of parameters, the
  36.  * register has to be initialized from the stack. The four cases are:
  37.  *
  38.  *    Locals    w/ other regs:    1 reference  required
  39.  *        no other regs:    4 references required
  40.  *    Parms    w/ other regs:    2 references required
  41.  *        no other regs:    6 references required
  42.  *
  43.  * The numbers above represent the break-even point based on a savings of
  44.  * 2 bytes per reference, and the incremental cost of adding "movem" or
  45.  * "move" instructions as needed.
  46.  *
  47.  * This optimizes for space only. To optimize for time, each reference would
  48.  * be weighted based on the loop nesting level at which it occurs.
  49.  *
  50.  * Modified by Detlef Wuerkner for AMIGA
  51.  * Changes marked with TETISOFT
  52.  */
  53.  
  54. #include "top.h"
  55.  
  56. #define    MAXLOCALS    100
  57.  
  58. static    struct    linfo {
  59.     long    offset;        /* offset from A6 */
  60.     int    size;        /* size of the object */
  61.     int    ref;        /* # of references to the local */
  62.     int    reg;        /* reg. we assigned it to */
  63.     int    flags;        /* length, etc. */
  64. } locals[MAXLOCALS];
  65.  
  66. #define    ALIASED        0x1    /* offset is aliased with another */
  67. #define    ADDR_TAKEN    0x2    /* address of the variable was taken */
  68.  
  69. #define    IS_LOCAL(x)    (locals[(x)].offset < 0)
  70. #define    IS_PARM(x)    (locals[(x)].offset > 0)
  71.  
  72. static    bool    paddr;        /* address of a parameter was taken */
  73. static    int    lcnt;        /* number of local variables we've seen */
  74. static    int    rcnt;        /* number of locals that got registerized */
  75.  
  76. static    int    omask, nmask;    /* old and new register masks */
  77.  
  78. /*
  79.  * addvar(size, off) - add a variable entry for the current function
  80.  *
  81.  * These come from hints the compiler gives us about local variables.
  82.  * We use the size and offset here to make sure we don't have aliasing
  83.  * problems with the local variables we want to registerize.
  84.  */
  85. void
  86. addvar(size, off)
  87. int    size;
  88. int    off;
  89. {
  90.     locals[lcnt].offset = off;
  91.     locals[lcnt].size = size;
  92.     locals[lcnt].flags = 0;
  93.     locals[lcnt].ref = 0;
  94.  
  95.     lcnt++;
  96. }
  97.  
  98. /*
  99.  * clrvar() - clear the variable list
  100.  */
  101. void
  102. clrvar()
  103. {
  104.     register int    i;
  105.  
  106.     /*
  107.      * re-initialize the local information
  108.      */
  109.     for (i=0; i < MAXLOCALS ;i++) {
  110.         locals[i].ref = 0;
  111.         locals[i].reg = -1;
  112.         locals[i].flags = 0;
  113.         locals[i].offset = 0;
  114.         locals[i].size = 0;
  115.     }
  116.     paddr = FALSE;
  117.     rcnt = lcnt = 0;
  118. }
  119.  
  120. /*
  121.  * setreg() - try to "registerize" local variables in the given function
  122.  */
  123. void
  124. setreg(bp)
  125. BLOCK    *bp;
  126. {
  127.     void    lcheck(), lassign(), lrewrite();
  128.  
  129.     lcheck(bp);
  130.     lassign();
  131.  
  132. #ifdef    DEBUG
  133.     if (debug)
  134.         dump_table();
  135. #endif
  136.  
  137.     if (rcnt > 0)
  138.         lrewrite(bp);
  139.  
  140.     s_reg += rcnt;        /* keep totals for accounting */
  141. }
  142.  
  143. /*
  144.  * lcheck() - scan for local variable references in the given function
  145.  */
  146. static void
  147. lcheck(bp)
  148. BLOCK    *bp;
  149. {
  150.     void    ckref();
  151.     register int    i;
  152.     register BLOCK    *cb;
  153.     register INST    *ci;
  154.  
  155.     for (cb = bp; cb != NULL ;cb = cb->next) {
  156.         for (ci = cb->first; ci != NULL ;ci = ci->next) {
  157.             ckref(ci, &ci->src);
  158.             ckref(ci, &ci->dst);
  159.         }
  160.     }
  161.  
  162.     /*
  163.      * Now figure out which registers are currently used.
  164.      */
  165.     ci = bp->first->next;
  166.  
  167.     if (ci != NULL && ci->opcode == MOVEM) {
  168.         if (ci->src.amode == REG)
  169.             omask = RM(ci->src.areg);
  170.         else
  171.             omask = stomask(ci->src.astr);
  172.     } else
  173.         omask = 0;
  174. }
  175.  
  176. /*
  177.  * ckref() - check for a local variable reference
  178.  *
  179.  * If a local variable reference is found, it's added to the table or
  180.  * (if already there) its reference count is incremented. If we're
  181.  * taking its address, note that too.
  182.  */
  183. static void
  184. ckref(ip, op)
  185. INST    *ip;
  186. struct    opnd    *op;
  187. {
  188.     register int    i;
  189.     register int    sz;
  190.  
  191. /* CHANGED BY TETISOFT (see inst.h) */
  192. /*    if (op->amode != REGID || op->areg != A6) */
  193.     if (op->amode != REGID || op->areg != FRAMEP)
  194.  
  195.         return;
  196.  
  197.     switch (ip->flags) {
  198.     case LENL:
  199.         sz = 4;
  200.         break;
  201.     case LENW:
  202.         sz = 2;
  203.         break;
  204.     case LENB:
  205.     default:        /* for LEA and PEA */
  206.         sz = 1;
  207.         break;
  208.     }
  209.  
  210.     /*
  211.      * is the local variable already in the table?
  212.      */
  213.     for (i=0; i < lcnt ;i++) {
  214.         if (locals[i].offset == op->disp && locals[i].size == sz) {
  215.             locals[i].ref++;
  216.             break;
  217.         }
  218.     }
  219.  
  220.     /*
  221.      * If not in the table, add an entry for it. If we add an entry
  222.      * here, it must be an alias for one of the entries we got via
  223.      * the compiler hints.
  224.      */
  225.     if (i == lcnt) {
  226.         locals[lcnt].offset = op->disp;
  227.         locals[lcnt].size = sz;
  228.         locals[lcnt].flags = 0;
  229.         locals[lcnt].ref = 1;
  230.  
  231.         lcnt++;
  232.     }
  233.  
  234.     if (ip->opcode == LEA || ip->opcode == PEA) {
  235.         locals[i].flags = ADDR_TAKEN;
  236.         /*
  237.          * If we took the address of a parameter, note that
  238.          * by setting 'paddr'.
  239.          */
  240.         if (IS_PARM(i))
  241.             paddr = TRUE;
  242.     }
  243. }
  244.  
  245. /*
  246.  * lassign() - assign local variable to registers
  247.  *
  248.  * Check for aliases, sort the table, and then decide how to assign
  249.  * the local variables to registers.
  250.  */
  251. static void
  252. lassign()
  253. {
  254.     void    ck_aliases(), sort_table(), do_sort();
  255.     register int    i;
  256.     register int    r;
  257.     int    minlref;    /* min. required references for a local */
  258.     int    minpref;    /* min. required references for a parameter */
  259.  
  260.     ck_aliases();        /* disqualify any "aliased" references */
  261.     sort_table();        /* and sort by reference count */
  262.  
  263.     /*
  264.      * If there were already "movem" instructions, then we should
  265.      * convert as many locals as possible to registers. If we're
  266.      * going to have to add the movem's, then we need at least 4
  267.      * references for this to be worthwhile. The 2 movem instructions
  268.      * take 8 bytes, and each reference conversion saves 2 bytes.
  269.      * This analysis optimizes for size.
  270.      */
  271.     minlref = (omask != 0) ? 1 : 4;
  272.     minpref = (omask != 0) ? 2 : 6;
  273.  
  274.     nmask = omask;
  275.  
  276. /* CHANGED BY TETISOFT (see inst.h) */
  277. /*    for (i=0, r=D3; r <= D7 ;) { */
  278.     for (i=0, r=DRV_START; r <= D7 ;) {
  279.  
  280.         /*
  281.          * If the register is already in use, skip it.
  282.          */
  283.         if (omask & RM(r)) {
  284.             r++;
  285.             continue;
  286.         }
  287.  
  288.         /*
  289.          * If no more eligible variables, then stop.
  290.          */
  291.         if (locals[i].ref <= 0)
  292.             break;
  293.  
  294.         /*
  295.          * If something meets the minimums, then assign it to
  296.          * the current register, and adjust the minimums.
  297.          */
  298.         if ((IS_LOCAL(i) && locals[i].ref >= minlref) ||
  299.             (IS_PARM(i)  && locals[i].ref >= minpref)) {
  300.             locals[i].reg = r;
  301.             nmask |= RM(r);
  302.             minlref = 1;
  303.             minpref = 2;
  304.             r++;
  305.             i++;
  306.         } else {
  307.             /*
  308.              * If we run into something that isn't referenced
  309.              * enough, disqualify it and re-sort. There might
  310.              * still be something else worth doing.
  311.              */
  312.             locals[i].ref = -locals[i].ref;
  313.             do_sort();
  314.         }
  315.     }
  316.     rcnt = i;
  317. }
  318.  
  319. /*
  320.  * ck_aliases() - check for aliases in the locals table
  321.  *
  322.  * An alias occurs when two different offsets off of A6 both reference
  323.  * the same local. This can happen when casting to a smaller type. Since
  324.  * these references would be a pain to rewrite, we just bag it.
  325.  */
  326. static void
  327. ck_aliases()
  328. {
  329.     static    bool    ck_aref();
  330.     register int    i;
  331.     register int    s;
  332.     register long    d;
  333.  
  334.     for (i=0; i < lcnt ;i++) {
  335.         d = locals[i].offset;
  336.         s = locals[i].size;
  337.  
  338.         if (ck_aref(d, s))
  339.             locals[i].flags |= ALIASED;
  340.     }
  341. }
  342.  
  343. /*
  344.  * ck_aref() - check for an aliased reference
  345.  */
  346. static bool
  347. ck_aref(d, len)
  348. register long    d;
  349. register int    len;
  350. {
  351.     register int    i;
  352.  
  353.     for (i=0; i < lcnt ;i++) {
  354.         if (locals[i].offset == d && locals[i].size == len)
  355.             continue;
  356.  
  357.         if (overlaps(d, len, locals[i].offset, locals[i].size)) {
  358.             locals[i].flags |= ALIASED;
  359.             return TRUE;
  360.         }
  361.     }
  362.     return FALSE;
  363. }
  364.  
  365. static bool
  366. overlaps(d1, s1, d2, s2)
  367. register long    d1, d2;
  368. int        s1, s2;
  369. {
  370.     register long    e1, e2;
  371.  
  372.     e1 = d1 + s1 - 1;
  373.     e2 = d2 + s2 - 1;
  374.  
  375.     if (d1 >= d2 && d1 <= e2)    /* d1 inside d2 <=> e2 */
  376.         return TRUE;
  377.  
  378.     if (e1 >= d2 && e1 <= e2)    /* e1 inside d2 <=> e2 */
  379.         return TRUE;
  380.  
  381.     return FALSE;
  382. }
  383.  
  384. static void
  385. sort_table()
  386. {
  387.     register int    i;
  388.  
  389.     /*
  390.      * Remove uninteresting references from consideration:
  391.      *
  392.      * 1. Variables whose address was taken, or are aliased with another.
  393.      * 2. Variables that don't fit in a register.
  394.      */
  395.     for (i=0; i < lcnt ;i++) {
  396.         if (locals[i].flags&(ADDR_TAKEN|ALIASED) || locals[i].size > 4)
  397.             locals[i].ref = -locals[i].ref;
  398.     }
  399.  
  400.     /*
  401.      * If paddr is set, remove any parameters from consideration. We
  402.      * have to do this so that things like printf (that take the address
  403.      * of a parameter and increment it) don't break. Only if no parameter
  404.      * addresses are taken, can we consider registerizing any of them.
  405.      */
  406.     if (paddr) {
  407.         for (i=0; i < lcnt ;i++) {
  408.             if (IS_PARM(i) && (locals[i].ref > 0))
  409.                 locals[i].ref = -locals[i].ref;
  410.         }
  411.     }
  412.  
  413.     do_sort();
  414. }
  415.  
  416. static void
  417. do_sort()
  418. {
  419.     register int    i;
  420.     struct    linfo    l;
  421.  
  422.     /*
  423.      * simple bubble sort
  424.      */
  425.     for (i=0; i < (lcnt-1) ;) {
  426.         if (locals[i].ref < locals[i+1].ref) {
  427.             l = locals[i];
  428.             locals[i] = locals[i+1];
  429.             locals[i+1] = l;
  430.             if (i > 0)
  431.                 i--;
  432.         } else
  433.             i++;
  434.     }
  435. }
  436.  
  437. /*
  438.  * lrewrite() - rewrite the function based on the new register assignments
  439.  *
  440.  * Fixing the references is easy, but we have to fix up (or add) the movem
  441.  * instructions as well. Also, we call addinits() to initialize any registers
  442.  * that will contain parameters.
  443.  */
  444. static void
  445. lrewrite(bp)
  446. BLOCK    *bp;
  447. {
  448.     void    fixref(), fixmove(), addmovem();
  449.     INST    *findlnk();
  450.     register int    i;
  451.     register BLOCK    *cb;
  452.     register INST    *ci;
  453.  
  454.     /*
  455.      * First, rewrite all the references to the locals that
  456.      * we've reassigned to registers.
  457.      */
  458.     for (cb = bp; cb != NULL ;cb = cb->next) {
  459.         for (ci = cb->first; ci != NULL ;ci = ci->next) {
  460.             fixref(&ci->src);
  461.             fixref(&ci->dst);
  462.         }
  463.     }
  464.  
  465.     /*
  466.      * If the movem's are there, just find them and fix up the
  467.      * register specs.
  468.      */
  469.     ci = bp->first->next;
  470.     if (ci != NULL && ci->opcode == MOVEM) {
  471.  
  472.         /*
  473.          * First, add the initialization instructions.
  474.          */
  475.         addinits(bp, bp->first->next);
  476.  
  477.         fixmove(&ci->src);
  478.  
  479.         for (cb = bp; cb != NULL ;cb = cb->next) {
  480.             if (cb->flags & B_RET) {
  481.                 for (ci=cb->last; ci != NULL ;ci=ci->prev) {
  482.                     if (ci->opcode == MOVEM) {
  483.                         fixmove(&ci->dst);
  484.                         return;
  485.                     }
  486.                 }
  487.             }
  488.         }
  489.         return;
  490.     }
  491.  
  492. #ifdef    DEBUG
  493.     if (debug)
  494.         printf("adding movem instructions\n");
  495. #endif
  496.     /*
  497.      * There aren't any movem instructions, so we have to add
  498.      * them here. What a pain...
  499.      */
  500.     addmovem(bp, findlnk(bp), TRUE);
  501.     addinits(bp, findlnk(bp)->next);
  502.  
  503.     for (cb = bp; cb != NULL ;cb = cb->next) {
  504.         if (cb->last->opcode == RTS) {
  505.             for (ci=cb->last; ci != NULL ;ci=ci->prev) {
  506.                 if (ci->opcode == UNLK) {
  507.                     addmovem(cb, ci, FALSE);
  508.                     return;
  509.                 }
  510.             }
  511.         }
  512.     }
  513.     /*
  514.      * Reaching this point would be an error, you'd think. It means
  515.      * we didn't find the exit from this function. Strangely enough,
  516.      * this can actually happen in routines with infinite loops.
  517.      * Since the "return" block isn't reachable, branch optimization
  518.      * actually removes it. So we can't consider it an error here if
  519.      * we don't find any "unlk" instruction.
  520.      */
  521. }
  522.  
  523. static void
  524. fixmove(op)
  525. struct    opnd    *op;
  526. {
  527.     char    *masktos();
  528.  
  529.     freeop(op);
  530.     op->amode = ABS;
  531.     op->astr = strsave(masktos(nmask));
  532. }
  533.  
  534. /*
  535.  * findlnk() - find the LINK instruction in the given block
  536.  *
  537.  * When profiling, the LINK isn't the first instruction in the entry
  538.  * block. This function lets us handle both cases cleanly.
  539.  */
  540. static    INST *
  541. findlnk(bp)
  542. BLOCK    *bp;
  543. {
  544.     INST    *ip;
  545.  
  546.     for (ip=bp->first; ip != NULL ;ip = ip->next) {
  547.         if (ip->opcode == LINK)
  548.             return ip;
  549.     }
  550.     return NULL;
  551. }
  552.  
  553. static void
  554. addmovem(bp, ip, is_entry)
  555. BLOCK    *bp;            /* block where we're working */
  556. INST    *ip;            /* instruction before or after the movem */
  557. bool    is_entry;        /* true if we're doing the entry code */
  558. {
  559.     char    *masktos();
  560.     register INST    *ni;
  561.     struct    opnd    *op;
  562.     register int    i;
  563.  
  564.     if (ip == NULL)        /* no LINK found */
  565.         return;
  566.  
  567.     /*
  568.      * Allocate and initialize a new instruction
  569.      */
  570.     ni = (INST *) alloc(sizeof(INST));
  571.  
  572.     ni->flags = LENL;
  573.     ni->opcode = MOVEM;
  574.     ni->live = 0;
  575.     ni->rref = ni->rset = 0;
  576.  
  577.     ni->src.areg = ni->dst.areg = 0;
  578.     ni->src.ireg = ni->dst.ireg = 0;
  579.     ni->src.disp = ni->dst.disp = 0;
  580.  
  581.     /*
  582.      * Set up the SP reference
  583.      */
  584.     op = (is_entry) ? &ni->dst : &ni->src;
  585.     op->amode = (is_entry) ? REGI|DEC : REGI|INC;
  586.     op->areg = SP;
  587.  
  588.     /*
  589.      * Set up the register spec operand
  590.      */
  591.     op = (is_entry) ? &ni->src : &ni->dst;
  592.  
  593.     op->amode = ABS;
  594.     op->astr = strsave(masktos(nmask));
  595.  
  596.     /*
  597.      * If there's only one register being used, we should really
  598.      * change the operand to be register direct. This way, the
  599.      * peephole optimization will turn the "movem" into a simple
  600.      * "move". Since we're adding an instruction here, we really
  601.      * need to make it as painless as possible.
  602.      */
  603.     if (rcnt == 1) {
  604.         free(op->astr);
  605.         op->amode = REG;
  606.  
  607.         for (i=D0; i <= D7 ;i++) {
  608.             if (nmask & RM(i)) {
  609.                 op->areg = i;
  610.                 break;
  611.             }
  612.         }
  613.     }
  614.  
  615.     /*
  616.      * Link the instruction into the block
  617.      */
  618.     if (is_entry) {
  619.         ni->next = ip->next;    /* link the MOVEM to its neighbors */
  620.         ni->prev = ip;
  621.  
  622.         ip->next = ni;        /* link its neighbors to the MOVEM */
  623.  
  624.         if (bp->last == ip)
  625.             bp->last = ni;
  626.         else
  627.             ni->next->prev = ni;
  628.     } else {
  629.         ni->next = ip;        /* link the MOVEM to its neighbors */
  630.         ni->prev = ip->prev;
  631.  
  632.         ip->prev = ni;        /* link its neighbors to the MOVEM */
  633.  
  634.         if (bp->first == ip)
  635.             bp->first = ni;
  636.         else
  637.             ni->prev->next = ni;
  638.     }
  639. }
  640.  
  641. static void
  642. addinits(bp, ip)
  643. BLOCK    *bp;            /* block where we're working */
  644. INST    *ip;            /* instruction before the moves */
  645. {
  646.     char    *masktos();
  647.     register INST    *ni;
  648.     struct    opnd    *op;
  649.     register int    i;
  650.  
  651.     if (ip == NULL)        /* no LINK found */
  652.         return;
  653.  
  654.     for (i=0; i < rcnt ;i++) {
  655.         /*
  656.          * If it's a local variable, we don't have to do anything.
  657.          */
  658.         if (IS_LOCAL(i))
  659.             continue;
  660.  
  661.         /*
  662.          * Allocate and initialize a new instruction
  663.          */
  664.         ni = (INST *) alloc(sizeof(INST));
  665.     
  666.         switch (locals[i].size) {
  667.         case 1:
  668.             ni->flags = LENB;
  669.             break;
  670.         case 2:
  671.             ni->flags = LENW;
  672.             break;
  673.         case 4:
  674.             ni->flags = LENL;
  675.             break;
  676.         default:
  677.             fprintf(stderr, "Invalid length\n");
  678. /* CHANGED BY TETISOFT */
  679.             /* exit(1); */
  680.             exit(EXIT_FAILURE);
  681.         }
  682.  
  683.         ni->opcode = MOVE;
  684.         ni->live = 0;
  685.         ni->rref = ni->rset = 0;
  686.         ni->src.ireg = ni->dst.ireg = 0;
  687.     
  688.         /*
  689.          * Set up the variable reference.
  690.          */
  691.         ni->src.amode = REGID;
  692.  
  693. /* CHANGED BY TETISOFT (see inst.h) */
  694. /*        ni->src.areg  = A6; */
  695.         ni->src.areg  = FRAMEP;
  696.  
  697.         ni->src.disp  = locals[i].offset;
  698.     
  699.         /*
  700.          * Set up the register spec operand
  701.          */
  702.         ni->dst.amode = REG;
  703.         ni->dst.areg  = locals[i].reg;
  704.         ni->dst.disp  = 0;
  705.  
  706.         /*
  707.          * Link the instruction into the block
  708.          */
  709.         ni->next = ip->next;    /* link MOVE to its neighbors */
  710.         ni->prev = ip;
  711.  
  712.         ip->next = ni;        /* link neighbors to the MOVE */
  713.  
  714.         if (bp->last == ip)
  715.             bp->last = ni;
  716.         else
  717.             ni->next->prev = ni;
  718.     }
  719. }
  720.  
  721. static void
  722. fixref(op)
  723. struct    opnd    *op;
  724. {
  725.     register int    i;
  726.  
  727. /* CHANGED BY TETISOFT (see inst.h) */
  728. /*    if (op->amode != REGID || op->areg != A6) */
  729.     if (op->amode != REGID || op->areg != FRAMEP)
  730.  
  731.         return;
  732.  
  733.     /*
  734.      * Does the reference need to be changed?
  735.      */
  736.     for (i=0; i < rcnt ;i++) {
  737.         if (locals[i].offset == op->disp) {
  738.             op->amode = REG;
  739.             op->areg = locals[i].reg;
  740.             return;
  741.         }
  742.     }
  743. }
  744.  
  745. /*
  746.  * stomask() - convert a register list to a mask
  747.  *
  748.  * Convert a string like "Rm-Rn/Ro-Rp" or "Rm-Rn" to the appropriate
  749.  * mask value.
  750.  */
  751. static int
  752. stomask(s)
  753. char    *s;
  754. {
  755.     register int    mask;
  756.     register char    *p;
  757.  
  758.     mask = dorspec(s);
  759.  
  760.     for (p=s; *p && *p != '/' ;p++)
  761.         ;
  762.  
  763.     if (*p == '/')
  764.         mask |= dorspec(p+1);
  765.  
  766.     return mask;
  767. }
  768.  
  769. /*
  770.  * dorspec() - convert a partial register spec
  771.  *
  772.  * Convert a string like "Rm" or "Rm-Rn" to a mask.
  773.  */
  774. static int
  775. dorspec(s)
  776. register char    *s;
  777. {
  778.     register int    base;
  779.     register int    m, n;
  780.     register int    mask;
  781.  
  782.     base = (s[0] == 'd') ? D0 : A0;
  783.  
  784.     m = s[1] - '0' + base;
  785.  
  786.     if (s[2] != '-')
  787.         return RM(m);
  788.  
  789.     n = s[4] - '0' + base;
  790.  
  791.     for (mask=0; m <= n ;m++)
  792.         mask |= RM(m);
  793.  
  794.     return mask;
  795. }
  796.  
  797. /*
  798.  * masktos() - convert a register mask to a descriptive string
  799.  *
  800.  * Generates a string of the form "Rm/Rn/Ro/..."
  801.  */
  802. static char *
  803. masktos(mask)
  804. register int    mask;
  805. {
  806.     static    char    buf[64];
  807.     register char    *p = buf;
  808.     register int    r;
  809.  
  810.     for (r = D0; r <= D7 ;r++) {
  811.         if (mask & RM(r)) {
  812.             if (p != buf)
  813.                 *p++ = '/';
  814.             *p++ = 'd';
  815.             *p++ = (r - D0) + '0';
  816.         }
  817.     }
  818.     for (r = A0; r <= A7 ;r++) {
  819.         if (mask & RM(r)) {
  820.             if (p != buf)
  821.                 *p++ = '/';
  822.             *p++ = 'a';
  823.             *p++ = (r - A0) + '0';
  824.         }
  825.     }
  826.     *p = '\0';
  827.  
  828.     return buf;
  829. }
  830.  
  831. #ifdef    DEBUG
  832. dump_table()
  833. {
  834.     register int    i;
  835.  
  836.     printf("%d local variables and parameters found\n", lcnt);
  837.     for (i=0; i < lcnt ;i++) {
  838.         printf("len = %d\n", locals[i].size);
  839.         printf("%02d: disp=%3ld, len=%d ref=%2d reg=%s",
  840.             i, locals[i].offset,
  841.             locals[i].size,
  842.             locals[i].ref,
  843.             locals[i].reg >= 0 ? masktos(RM(locals[i].reg)) : "-");
  844.         if (locals[i].flags & ADDR_TAKEN)
  845.             printf(" ADDR_TAKEN");
  846.         if (locals[i].flags & ALIASED)
  847.             printf(" ALIASED");
  848.         printf("\n");
  849.     }
  850. }
  851. #endif
  852.